/*
 * User: rant
 * Date: 07/02/2007
 *
 *
 */
package laMalinche.rete.nodes.editing;

import laMalinche.editableContext.AboutToChangeManager;
import laMalinche.rete.conditions.BasicEditingCondition;
import laMalinche.rete.nodes.BasicConditionNode;
import laMalinche.rete.nodes.EditingConditionNode;
import laMalinche.rete.nodes.StopEvaluatingException;
import laMalinche.rete.nodes.tailing.*;
import laMalinche.rete.rule.ActObservable;
import laMalinche.rete.rule.BusinessRuleType;
import laMalinche.rete.rule.EditingCondition;
import laMalinche.testingPlayGround.testModel.VOEntity;

import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;

import java.util.*;
import static java.lang.Boolean.*;

/**
 This node has a conditionionNode. it registerCertaintyManager acording to the BusinessRuleType of the Production of the Condition
 */
public class BasicEditingConditionNode<T extends EditingCondition> extends BasicConditionNode<T> implements EditingConditionNode<T >, FutureEditingInformer {

    private static final Logger log = LogManager.getLogger( BasicEditingConditionNode.class);
    private AboutToChangeManager aboutToChangeManager;
	private CertaintyManager certaintyManager;
	private BusinessRuleType businessRuleType;
	private boolean updatingFalse;

    public BusinessRuleType getBusinessRuleType() {
		return businessRuleType;
	}

	public void setAboutToChangeManager(final AboutToChangeManager aboutToChangeManager) {
		this.aboutToChangeManager = aboutToChangeManager;
	}

//	@Override
//	@NotNull
//	public T getCondition() {
//		return (EditingCondition) super.getCondition();
//	}

	public void setBusinessRuleType(BusinessRuleType businessRuleType) {
		this.businessRuleType = businessRuleType;
	}

	private void registerCertaintyManager() {
		certaintyManager = createCertaintyManager();
		certaintyManager.setAboutToChangeManager(getAboutToChangeManager());
		getAboutToChangeManager().addAboutToChangeListener(certaintyManager);
		certaintyManager.setConditionNode(getCondition().getConditionNode());
		certaintyManager.addWorkingMemoryListener(this);
		final ActObservable actObservable = ((ActObservableHolder) getCondition().getConditionNode()).getActObservable();
		TailingManager basicTailingManager = actObservable.getTailingManager();
		basicTailingManager.addPropertyTouchedListener(certaintyManager);
	}

	private CertaintyManager createCertaintyManager() {
		if (BusinessRuleType.suggestedValue.equals(getBusinessRuleType())) {
			return new IntoTaskCertaintyManager();
		}
		return new OpenFieldCertaintyManager();
	}

	public void update() {
		if (updatingFalse) {
            return;
        }
		Boolean met = getCondition().getConditionNode().isConditionMet();
		if (TRUE.equals(met)) {
			try {
			met = getCondition().isComplied();
			} catch(StopEvaluatingException e){
				log.info("production rule failed, condition was not evaluated");
				met = null;
			}
		}
		setConditionMet(met);
	}

	public void initialize() {
	}

	@Override
	public void setConditionMet(Boolean value) {
		updatingFalse = true;
		super.setConditionMet(value);
		updatingFalse = false;
	}

	@Override
	public void detach() {
		if (certaintyManager != null) {
			certaintyManager.detach();
		}
		super.detach();
	}

	@Override
	public void setCondition(T condition) {
		super.setCondition(condition);
		registerCertaintyManager();
		((WorkingMemoryListenableIfc) getCondition().getConditionNode()).addWorkingMemoryListener(this);
		((BasicEditingCondition) getCondition()).setCertaintyManager(certaintyManager);
	}

	@Override
	public void workingMemoryChange(WorkingMemoryEvent event) {
		final WorkingMemoryListenable memory = event.getMemory();
		if (memory.equals(certaintyManager)) {
			final Boolean aBoolean = event.isConditionMet();
//			if (Boolean.TRUE.equals(aBoolean)) {
				setConditionMet(aBoolean);
//			}
		} else {
			super.workingMemoryChange(event);
		}
	}

	private AboutToChangeManager getAboutToChangeManager() {
		return aboutToChangeManager;
	}

	 String getOpenTouchedElement(){
		return  certaintyManager.getOpenTouchedElement();
	 }

	// added for informer
	public boolean willBeActive(Set<String> aboutTochange) {
			final String openField = getOpenTouchedElement();
			boolean isActive = isNotFalse() && openField ==null;
			boolean isGoingTobeActive = falseButGoingToBeActive(aboutTochange);
			boolean isGoingToBeCertain = ((openField != null) &&
					aboutTochange.contains(openField));
			return isActive || isGoingTobeActive || isGoingToBeCertain;
		}


		public Set<String> activatedBy(Set<String> aboutTochange) {
			final String element = getOpenTouchedElement();
				final Set<String> touched = touched(aboutTochange);
				if (element == null  || aboutTochange.contains(element)) {
							return touched;
				}
			return null;
		}

		private boolean falseButGoingToBeActive(final Set<String> aboutTochange) {
			return !isNotFalse() && getOpenTouchedElement() == null && !touched(aboutTochange).isEmpty();
		}

		private boolean isNotFalse() {
			return isConditionMet() != FALSE;
		}

		private Set<String> touched(final Set<String> aboutTochange) {
			final Set<String> touched = new HashSet<String>();
			// getWorkingMemory
			final List<VOEntity> list = ((OpenFieldCertaintyManager)certaintyManager).getWorkingMemory().getRelevantFields();
			for (VOEntity s : list) {
				final String field = s.getOgnl();
				for(String change: aboutTochange){
					if(change.equalsIgnoreCase(field)){
							touched.add(field);
//							continue;
					}
				}
//				if (aboutTochange.contains(field))
//					touched.add(field);
			}
			return touched;
		}



}